using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.IO;
using System.IO.IsolatedStorage;
using System.Runtime.Serialization;

#if WINDOWS
using System.Runtime.Serialization.Formatters.Binary;
#endif

#if XBOX
using EasyStorage;
#endif

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using GameStateManagement;
using Frogger_Input;

// ****************************************************************************** //
//        This component handle all the main menu entries and functions           //
// ****************************************************************************** //

namespace Frogger_Game
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class Main_Menu : Microsoft.Xna.Framework.DrawableGameComponent
    {
          public Main_Menu(Game game)
            : base(game)
        {
        }

        #region Fields

        //graphics assets
        Texture2D background;
        SpriteBatch spriteBatch;
        SpriteFont font;

        //sound component
        SoundData sound;

#if XBOX
        //Device for saving data in Xbox360 console
        IAsyncSaveDevice saveDevice;
        SharedSaveDevice sharedSaveDevice;
#endif

        //menu & input elements
        List<MenuEntry> menuEntries;
        int selectedEntry = 0;
        InputState input;
        InputButtons ib;

        //messagebox component
        MessageBoxScreen messageBox;

        #endregion

        #region Handle Input
        /// <summary>
        /// Event handler for when the Play Game menu entry is selected.
        /// </summary>
        void PlayGameMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            sound.StopSound(sound.ThemeInstance);
#if XBOX
                Game.Components.Remove(sharedSaveDevice);
#endif
            while (Game.Components.Count > 1) //1 because we want to keep sound alive
            {
                ((GameComponent)Game.Components[1]).Dispose();
            }

            Game.Services.RemoveService(typeof(InputState));
            Game.Services.RemoveService(typeof(InputButtons));
            Game.Components.Add(new InputHandler(Game));
            Game.Components.Add(new GameLogic(Game));
            Game.Components.Add(new Rendering(Game));
            Game.Components.Add(new Rendering3D(Game));
        }


        /// <summary>
        /// Event handler for when the Load Game menu entry is selected.
        /// </summary>
        void LoadGameMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            sound.StopSound(sound.ThemeInstance);
#if XBOX
            Game.Components.Remove(sharedSaveDevice);
#endif
            while (Game.Components.Count > 1)
            {
                ((GameComponent)Game.Components[1]).Dispose();
            }

            Game.Services.RemoveService(typeof(InputState));
            Game.Services.RemoveService(typeof(InputButtons));
            Game.Components.Add(new InputHandler(Game));
            Game.Components.Add(new GameLogic(Game));
            Game.Components.Add(new Rendering(Game));
            Game.Components.Add(new Rendering3D(Game));

#if WINDOWS
            using (IsolatedStorageFile IS = IsolatedStorageFile.GetUserStoreForDomain())
            {
                if (IS.FileExists("savegame.sav"))
                {
                    using (IsolatedStorageFileStream stream = IS.OpenFile("savegame.sav", FileMode.Open))
                    {
                        BinaryFormatter binformat = new BinaryFormatter();
                        FrogSimulation.reset_state();
                        GameState.game_state = (GameState.GameState)binformat.Deserialize(stream);
                    }
                }
            }

#elif XBOX
            if (saveDevice.FileExists("FroggerSaveGame","savegame.sav"))
            {
                saveDevice.Load(
                    "FroggerSaveGame",
                    "savegame.sav",
                    stream =>
                    {
                        using (StreamReader reader = new StreamReader(stream))
                        {
                            FrogSimulation.reset_state();
                            GameState.game_state.count_win = int.Parse(reader.ReadLine());
                            GameState.game_state.level = int.Parse(reader.ReadLine());
                            GameState.game_state.Life = int.Parse(reader.ReadLine());
                            GameState.game_state.river_position = bool.Parse(reader.ReadLine());
                            GameState.game_state.time_counter = float.Parse(reader.ReadLine());
                            GameState.game_state.winbox_check[0] = bool.Parse(reader.ReadLine());
                            GameState.game_state.winbox_check[1] = bool.Parse(reader.ReadLine());
                            GameState.game_state.winbox_check[2] = bool.Parse(reader.ReadLine());
                            GameState.game_state.winbox_check[3] = bool.Parse(reader.ReadLine());
                            GameState.game_state.winbox_check[4] = bool.Parse(reader.ReadLine());
            
                        }
                    });
            }
#endif
        }


        /// <summary>
        /// Event handler for when the Options menu entry is selected.
        /// </summary>
        void OptionsMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
#if XBOX
            Game.Components.Remove(sharedSaveDevice);
#endif
            while (Game.Components.Count > 1)
            {
                ((GameComponent)Game.Components[1]).Dispose();
            }

            Game.Components.Add(new Options_Menu(Game));
        }


        /// <summary>
        /// Event handler for when the Exit Game menu entry is selected.
        /// </summary>
        void ExitSelected(object sender, PlayerIndexEventArgs e)
        {
            messageBox = new MessageBoxScreen(Game, "Are you sure you want to exit the game?", true);
            Game.Components.Add(messageBox);
            messageBox.Accepted += ConfirmExitGame;
            messageBox.Cancelled += CancelExitGame;
        }

        /// <summary>
        /// Event handler for when the Exit Game message box is confirmed.
        /// </summary>
        void ConfirmExitGame(object sender, PlayerIndexEventArgs e)
        {
#if XBOX
            Game.Components.Remove(sharedSaveDevice);
#endif
            // in this case, given that we have to close the whole game
            // we dispose also the sound component (Components[1]).
            while (Game.Components.Count > 0)
            {
                ((GameComponent)Game.Components[0]).Dispose();
            }

            Game.Components.Clear();
            Game.Exit();
        }


        /// <summary>
        /// Event handler for when the Exit Game message box is cancelled.
        /// </summary>
        void CancelExitGame(object sender, PlayerIndexEventArgs e)
        {
            messageBox.Dispose();
            Game.Components.Remove(messageBox);
        }

        #endregion


        /// <summary>
        /// Initialize all needed variables
        /// </summary>
        public override void Initialize()
        {
            //initialize sound data
            sound = (SoundData)Game.Services.GetService(typeof(SoundData));
            sound.PlaySound(sound.ThemeInstance);

            font = Game.Content.Load<SpriteFont>("menufont");

            //initialize input services
            input = (InputState)Game.Services.GetService(typeof(InputState));
            ib = (InputButtons)Game.Services.GetService(typeof(InputButtons));

            //create menu entries
            menuEntries = new List<MenuEntry>();
            MenuEntry playGameMenuEntry = new MenuEntry("Play Game",font);
            MenuEntry continueMenuEntry = new MenuEntry("Load Game", font);
            MenuEntry optionsMenuEntry = new MenuEntry("Options", font);
            MenuEntry creditsMenuEntry = new MenuEntry("Credits", font);
            MenuEntry exitMenuEntry = new MenuEntry("Exit", font);

            // Hook up menu event handlers.
            playGameMenuEntry.Selected += PlayGameMenuEntrySelected;
            continueMenuEntry.Selected += LoadGameMenuEntrySelected;
            optionsMenuEntry.Selected += OptionsMenuEntrySelected;
            //creditsMenuEntry.Selected += CreditsMenuEntrySelected; //not active in this version
            exitMenuEntry.Selected += ExitSelected;

            // Add entries to the menu.
            menuEntries.Add(playGameMenuEntry);
            menuEntries.Add(continueMenuEntry);
            menuEntries.Add(optionsMenuEntry);
            menuEntries.Add(creditsMenuEntry);
            menuEntries.Add(exitMenuEntry);

            //set volume for music and effect to the global value
            sound.SetMusicVolume(Game1.musicVolume);
            sound.SetEffectsVolume(Game1.effectsVolume);


#if XBOX

            // we can set our supported languages explicitly or we can allow the
            // game to support all the languages. the first language given will
            // be the default if the current language is not one of the supported
            // languages. this only affects the text found in message boxes shown
            // by EasyStorage and does not have any affect on the rest of the game.
            EasyStorageSettings.SetSupportedLanguages(Language.English, Language.Italian);

            // create and add our SaveDevice
            sharedSaveDevice = new SharedSaveDevice();
            Game.Components.Add(sharedSaveDevice);

            // make sure we hold on to the device
            saveDevice = sharedSaveDevice;

            // hook two event handlers to force the user to choose a new device if they cancel the
            // device selector or if they disconnect the storage device after selecting it
            sharedSaveDevice.DeviceSelectorCanceled += (s, e) => e.Response = SaveDeviceEventResponse.Force;
            sharedSaveDevice.DeviceDisconnected += (s, e) => e.Response = SaveDeviceEventResponse.Force;

            // prompt for a device on the first Update we can
            sharedSaveDevice.PromptForDevice();

#endif


            base.Initialize();
        }

        /// <summary>
        /// Load the graphics content
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            background = Game.Content.Load<Texture2D>("menu_back");
        }

        /// <summary>
        /// Update method
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            input.Update();
            PlayerIndex playerIndex = PlayerIndex.One;

            if (ib.menuUp.Evaluate(input, null, out playerIndex))
            {
                selectedEntry--;

                if (selectedEntry < 0)
                    selectedEntry = menuEntries.Count - 1;
            }

            // Move to the next menu entry?
            if (ib.menuDown.Evaluate(input, null, out playerIndex))
            {
                selectedEntry++;

                if (selectedEntry >= menuEntries.Count)
                    selectedEntry = 0;
            }

            // If a menu entry is selected
            if (ib.menuSelect.Evaluate(input, null, out playerIndex))
            {
                menuEntries[selectedEntry].OnSelectEntry(playerIndex);
            }

            // Update all menu entries
            for (int i = 0; i < menuEntries.Count; i++)
            {
                bool isSelected = (i == selectedEntry);

                menuEntries[i].Update(isSelected, gameTime);
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// Draw method
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            var height = GraphicsDevice.Viewport.Height;

            Vector2 position = new Vector2(0f, 370 * (float)height/1080.0f);

            // update each menu entry's location in turn
            for (int i = 0; i < menuEntries.Count; i++)
            {
                MenuEntry menuEntry = menuEntries[i];

                // each entry is to be centered horizontally
                position.X = GraphicsDevice.Viewport.Width / 2 - menuEntry.GetWidth() / 2;

                 // set the entry's position
                menuEntry.Position = position;

                // move down for the next entry
                position.Y += 120 * (float)height / 1080.0f;
            }

            spriteBatch.Begin();
            spriteBatch.Draw(background, new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), 
                new Rectangle(0, 0, background.Width, background.Height), Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, 0);

            // Draw each menu entry in turn.
            for (int i = 0; i < menuEntries.Count; i++)
            {
                MenuEntry menuEntry = menuEntries[i];

                bool isSelected = (i == selectedEntry);

                //Need this check to scale down the font size for very low resolutions
                //Bad solution, but better than nothing
                if (GraphicsDevice.Viewport.Width > 600)
                    menuEntry.Draw(isSelected, gameTime, spriteBatch, font,1.0f);
                else
                    menuEntry.Draw(isSelected, gameTime, spriteBatch, font,0.5f);
            }
            spriteBatch.End();
            base.Draw(gameTime);
        }

    }


}
